;
(function(global, factory) {

	if (typeof define === 'function' && define.amd) {
		// 添加到amd中，并加载依赖
		define([ 'jquery', 'suredy', 'bootstrap' ], function(_$, _$d) {
			return factory(global, _$, _$d);
		});
	} else if (!global.jQuery || !global.Suredy) {
		throw new Error("Suredy.Tree requires a jQuery and a Suredy");
	} else {
		factory(global, global.jQuery, global.Suredy);
	}

})(this, function(window, $, $d, undefined) {

	var plugin_flag = '_suredy_tree_';
	var event_node_click = 'suredy.tree.node.click';
	var node_data = '__node_data__';

	// bind data to node
	var _bind_node_data = function($span, data) {
		var d = $.extend({}, data);

		d.ud = d.data;
		d['_icon'] = d.icon;
		d['_text'] = d.text;

		// remove children data
		delete d.children;
		delete d.data;

		// store data to tree node
		$span.data(node_data, d);
	};

	// listening collapse event
	var _bind_collapse_event = function($ele) {
		$ele.find('li>span+ul.collapse').on('shown.bs.collapse', function() {
			var $this = $(this);

			if ($this.hasClass('in'))
				$this.parent().find('>span.folder').addClass('opened');
		});

		$ele.find('li>span+ul.collapse').on('hidden.bs.collapse', function() {
			var $this = $(this);

			if (!$this.hasClass('in'))
				$this.parent().find('>span.folder').removeClass('opened');
		});

		return true;
	};

	// bind node click
	var _bind_node_click = function($span) {
		// bind tree node click event
		$span.on('click.suredy.tree.node', function(event) {
			var $this = $(this);
			var live = $this.data('live');
			var tree = $this.parents('.suredy-tree.tree');
			var target = $(event.target);

			if (!live)
				return true;

			if ($this.hasClass('folder')) {
				if (target.is($span.find('>.tree-icon.pull-left').first())) {
					return true;
				} else if (target.is($span.find('>.tree-icon.pull-left').last())) {
					return true;
				}
			}

			if (tree.hasClass('toggle-active')) {
				// toggle active on this node
				$this.toggleClass('active');
			} else {
				$this.addClass('active');
			}

			// change active
			if (!tree.hasClass('multiselect') && $this.hasClass('active')) {
				tree.find('span.active').each(function(i) {
					if (this === $this.get(0))
						return true;

					$(this).removeClass('active');
				});
			}

			// trigger node click
			$this.trigger(event_node_click);

			return false;
		});
	};

	// if node can be active
	var _is_node_can_active = function(leaf, leafCheckbox, canLeafActive, folderCheckbox, canFolderActive) {
		// leaf node
		if (!!leaf)
			return !!leafCheckbox || !!canLeafActive;

		// folder node
		return !!folderCheckbox || !!canFolderActive;
	};

	// push dom to tree
	var _push_tree = function(container, data, options, level, collapse) {
		if (!(container instanceof $) || container.length !== 1)
			return false;

		if (!data || !$.isArray(data))
			return false;

		if (data.length === 0)
			return true;

		var autoCollapse = !!options.autoCollapse;
		var multiselect = !!options.multiselect;
		var leafCheckbox = !!options.leafCheckbox;
		var folderCheckbox = !!options.folderCheckbox;
		var canLeafActive = !!options.canLeafActive;
		var canFolderActive = !!options.canFolderActive;
		var collapseAll = !!options.collapseAll;
		var size = $.type(options.size) === 'string' ? options.size : 'normal';
		var style = $.type(options.style) === 'string' ? options.style : 'normal';
		var showTitle = !!options.showTitle;
		var toggleActive = !!options.toggleActive;

		level = $.type(level) === 'number' ? level : 0;
		collapse = collapseAll || !!collapse;

		var id_ul = $d.randomNumberString();
		var tmp_ul = '<ul class="{cls}{in}{size}{style}{multiselect}{toggleActive}" id="' + id_ul + '"></ul>';

		// ul class
		tmp_ul = tmp_ul.replace(/\{cls\}/g, level === 0 ? 'suredy-tree tree' : 'collapse');

		// ul in
		tmp_ul = tmp_ul.replace(/\{in\}/g, level === 0 ? '' : (collapse ? '' : ' in'));

		// ul size
		tmp_ul = tmp_ul.replace(/\{size\}/g, level === 0 ? ' tree-' + size : '');

		// ul style
		tmp_ul = tmp_ul.replace(/\{style\}/g, level === 0 ? ' ' + style : '');

		// ul multiselect
		tmp_ul = tmp_ul.replace(/\{multiselect\}/g, level === 0 ? (multiselect ? ' multiselect' : '') : '');

		// ul toggleActive
		tmp_ul = tmp_ul.replace(/\{toggleActive\}/g, level === 0 ? (toggleActive ? ' toggle-active' : '') : '');

		container.append(tmp_ul);

		var ul = $('ul#' + id_ul, container);

		$.each(data, function(i) {
			var d = data[i];

			var withChildren = !!d.children;
			var active = !!d.active || !!d.checked;
			var canActive = _is_node_can_active(!withChildren, leafCheckbox, canLeafActive, folderCheckbox, canFolderActive);
			var text = d.text || 'Tree Node';
			var opened = !(collapseAll || d.collapse);

			var id_li = $d.randomNumberString();
			var tmp_li = '<li{cls} id="' + id_li + '"></li>';

			// li class
			tmp_li = tmp_li.replace(/\{cls\}/g, (autoCollapse && withChildren) ? ' class="panel"' : '');

			ul.append(tmp_li);

			var li = $('li#' + id_li, ul);

			var tmp_span = '<span class="tree-node{active}{folder}{opened}{empty}"{toggle}{target}{parent}{padding} data-live="{live}"{title}></span>';

			// span active
			tmp_span = tmp_span.replace(/\{active\}/g, (active && canActive) ? ' active' : '');

			// span folder
			tmp_span = tmp_span.replace(/\{folder\}/g, withChildren ? ' folder' : ' leaf');

			// span opened
			tmp_span = tmp_span.replace(/\{opened\}/g, (withChildren && opened) ? ' opened' : '');

			// span empty
			tmp_span = tmp_span.replace(/\{empty\}/g, (withChildren && $.isEmptyObject(d.children)) ? ' empty' : '');

			// span data-toggle
			tmp_span = tmp_span.replace(/\{toggle\}/g, withChildren ? ' data-toggle="collapse"' : '');

			// span data-target
			tmp_span = tmp_span.replace(/\{target\}/g, withChildren ? ' data-target="#' + id_li + '>ul"' : '');

			// span data-parent
			tmp_span = tmp_span.replace(/\{parent\}/g, withChildren ? ' data-parent="#' + id_ul + '"' : '');

			// span padding
			tmp_span = tmp_span.replace(/\{padding\}/g, ' style="padding-left: ' + (20 * level + 5) + 'px"');

			// span live
			tmp_span = tmp_span.replace(/\{live\}/g, canActive);

			// span title
			tmp_span = tmp_span.replace(/\{title\}/g, showTitle ? ' title="' + text + '"' : '');

			li.append(tmp_span);

			var span = li.find('>span');

			span.text(text);

			// node icon
			span.prepend('<i class="tree-icon pull-left ' + (d.icon || 'icon-th') + '"></i>');

			// checkbox iconicon
			if ((leafCheckbox && !withChildren) || (folderCheckbox && withChildren))
				span.prepend('<i class="tree-icon tree-checkbox pull-left icon-check-empty"></i>');

			// collapse icon
			span.prepend('<i class="tree-icon pull-left ' + (withChildren ? 'icon-angle-right' : '') + '"></i>');

			_bind_node_data(span, d);

			_bind_node_click(span);

			if (withChildren)
				_push_tree(li, d.children, options, level + 1, !opened);
		});

		return true;
	};

	var Tree = function($ele, resource, options) {
		if (!$ele || !($ele instanceof $) || $ele.length !== 1)
			throw new Error('Need a jQuery(only one) object container.');

		if ($.type(resource) !== 'function' && $.type(resource) !== 'string' && !$.isArray(resource))
			throw new Error('Need a data resource. It can be a function with no params, a url or a array data.');

		return this.prepar($ele, resource, options);
	};

	Tree.prototype = {
		constructor : Tree,
		prepar : function($ele, resource, options) {
			this.data = {};

			this.ele = $ele;

			this.resource = resource;

			this.options = $.extend({
				allowEmpty : true,
				autoCollapse : true,
				multiselect : false,
				leafCheckbox : false,
				folderCheckbox : false,
				canLeafActive : true,
				canFolderActive : true,
				collapseAll : false,
				inContainer : true,
				size : 'normal', // xl > lg > normal > sm. default is normal
				style : 'normal', // normal|file|department
				showTitle : false,
				toggleActive : true
			}, options);

			// clean
			this.ele.empty();

			// store data to jQuery
			this.ele.data(plugin_flag, this);

			return this;
		},
		draw : function() {
			var data = undefined;

			// get tree menu data
			if ($.type(this.resource) === 'string') {
				data = $d.loadData(this.resource);
			} else if ($.type(this.resource) === 'function') {
				data = this.resource();
			} else {
				data = this.resource;
			}

			if (!data)
				return;

			// convert object to array
			if ($.type(data) === 'object')
				data = [ data ];

			if (!$.isArray(data))
				throw new Error('Invalid tree menu data.');

			if (!this.options.allowEmpty && data.length === 0)
				throw new Error('The tree data is empty. But you do not allow that.');

			_push_tree(this.ele, data, this.options);

			if (!!this.options.inContainer) {
				this.ele.find('li > span').css('border-left-width', '0px');
				this.ele.find('li > span').css('border-right-width', '0px');
				this.ele.find('li > span').first().css('border-top-width', '0px');
				this.ele.find('li > span').last().css('border-bottom-width', '0px');
			}

			_bind_collapse_event(this.ele);

			return this;
		}
	};

	// 定义Suredy.list对象
	$d.Tree = function(selector, resource, options) {
		if (!selector)
			throw new Error('Error! Need a css selector parameter first.');

		var $container = selector;

		if (!($container instanceof jQuery)) {
			$container = $(selector);
		}

		if ($container.length == 0)
			throw new Error('Error! Can not find tree menu container.');

		return $container.each(function(i) {
			var $tree = $(this);
			var tree = new Tree($tree, resource, options);

			// draw tree
			tree.draw();
		});
	};

	$.extend($d.Tree, {
		nodeClick : event_node_click,
		nodes : function(tree) {
			if (!tree)
				throw new Error('Invalid Suredy Tree.');

			if (!(tree instanceof jQuery))
				tree = $(tree);

			return $('span.tree-node', tree);
		},
		data : function(node, key) {
			if (!node)
				throw new Error('Invalid Suredy Tree node.');

			if (!(node instanceof jQuery))
				node = $(node);

			if (!node.hasClass('tree-node'))
				throw new Error('It is not a Suredy Tree node.');

			var data = node.data(node_data);

			if (!data)
				return undefined;

			var userData = data.ud;

			if (!key)
				return userData;

			return (userData && userData[key]) || (data && data[key]);
		},
		checked : function(tree) {
			if (!tree)
				throw new Error('Invalid Suredy Tree.');

			if (!(tree instanceof jQuery))
				tree = $(tree);

			return $('span.tree-node.active', tree).get();
		},
		isActive : function(node) {
			if (!node)
				throw new Error('Invalid Suredy Tree node.');

			if (!(node instanceof jQuery))
				node = $(node);

			if (!node.hasClass('tree-node'))
				throw new Error('It is not a Suredy Tree node.');

			return node.hasClass('active');
		},
		setActive : function(node, trigger) {
			if (!node)
				throw new Error('Invalid Suredy Tree node.');

			if (!(node instanceof jQuery))
				node = $(node);

			if (!node.hasClass('tree-node'))
				throw new Error('It is not a Suredy Tree node.');

			node.addClass('active');

			if (!!trigger)
				node.trigger(event_node_click);

			return node;
		}
	});

	$d.Tree.active = $d.Tree.checked;
	$d.Tree.isChecked = $d.Tree.isActive;

	// 对象属性
	$d.Tree.prototype = {
		constructor : $d.Tree
	};

	// 返回Suredy对象
	return $d.Tree;

});